
#include "pins.h"

#define VERSION         "1.0"
#define IDENT           "EEPROM_STM"

// Buffers
#define BUFF_LEN 128
#define CMD_LEN 22

uint8_t pageBuffer[BUFF_LEN];                  // page buffer
char    cmdBuffer[CMD_LEN];                    // command buffer


void setup() {
  read_mode();

  //地址线
  addressOutput();

  pinMode(chipWE, OUTPUT);
  pinMode(chipCE, OUTPUT);
  pinMode(chipOE, OUTPUT);
  digitalWrite(chipWE, HIGH);
  digitalWrite(chipCE, HIGH);
  digitalWrite(chipOE, HIGH);

  // 初始化地址
  setAddress(0);

  //Serial.begin(1000000);
  Serial.begin(115200);
}


void check(uint32_t chipId) {
    Serial.println(F("ok"));
}



// 读取芯片内容(读取的时候ce oe全部拉低，改变地址就可以输出)
void read29(uint32_t addr, uint32_t count) {
  //加上y参数的扩展地址
  read_mode();
  
  digitalWrite(chipWE, HIGH);
  digitalWrite(chipCE, LOW);
  digitalWrite(chipOE, LOW);

  while (count-- > 0) {
    Serial.write(getByte(addr++));
    delayMicroseconds(1);
    if (addr & 0xfff == 0)delay(1);
  }
  digitalWrite(chipOE, HIGH);
  digitalWrite(chipCE, HIGH);
}

void write29(uint32_t addr, uint32_t total, uint8_t typee) {
  delayMicroseconds(1);
  for (uint8_t i = 0; i < BUFF_LEN; i++) {
    while (!Serial.available());
      pageBuffer[i] = Serial.read();
  }

  chip29();

  write_mode();
  digitalWrite(chipOE, HIGH);
  digitalWrite(chipWE, HIGH);

  // A29 MX29 SSF39
  if (typee == 29 || typee == 39) {
    digitalWrite(chipCE, LOW);
    delayMicroseconds(10);

    if (addr == 0) {
//      for (uint8_t i = 0; i < BUFF_LEN; i++) {
//        setByte (0x5555, 0xaa);
//        setByte (0x2aaa, 0x55);
//        setByte (0x5555, 0xa0);
//        setByte (addr + i, pageBuffer[i]);
//        delayMicroseconds(2);
//      }
      
      digitalWrite(chipCE, LOW);
      setByte (0, 0xf0);
      digitalWrite(chipCE, HIGH);
      delay(100);
    }
    digitalWrite(chipCE, LOW);

    for (uint8_t i = 0; i < BUFF_LEN; i++) {
      setByte (0x5555, 0xaa);
      setByte (0x2aaa, 0x55);
      setByte (0x5555, 0xa0);
      setByte (addr + i, pageBuffer[i]);
      //delayMicroseconds(1);

//      delay55ns;
      delay110ns;
      
      if(i==BUFF_LEN){
        read_mode();
        digitalWrite(chipOE, LOW);
        while(getByte(addr + BUFF_LEN)!=pageBuffer[BUFF_LEN]){delay14ns;}
        digitalWrite(chipOE, HIGH);
        write_mode();
      }
    } 
    delay(1);
    // Winbond 29 特殊的 128 字节写入
  } else if (typee == 9) {
    digitalWrite(chipCE, LOW);
    setByte (0x5555, 0xaa);
    setByte (0x2aaa, 0x55);
    setByte (0x5555, 0xa0);
    for (uint8_t i = 0; i < BUFF_LEN; i++) {
      setByte(addr + i, pageBuffer[i]);
      //delayMicroseconds(1);
      delay110ns;
    }
    delayMicroseconds(10);
    
    read_mode();
    digitalWrite(chipOE, LOW);
    while(getByte(addr + BUFF_LEN)!=pageBuffer[BUFF_LEN]);
    digitalWrite(chipOE, HIGH);
    write_mode();
      
    // 其他芯片报错
  } else {
    Serial.print(F("Not support type for write:"));
    Serial.println(typee);
    return;
  }

  digitalWrite(chipWE, HIGH);
  digitalWrite(chipOE, HIGH);
  digitalWrite(chipCE, HIGH);
  delayMicroseconds(100);

  if(addr<8192|| (addr & 0xffff)==0xffff){
    check_buff29(addr, typee);  
  }else{
    Serial.println("ok");
  }
}

void check_buff29(uint32_t addr, uint8_t typee) {

  read_mode();
  //读数据校验
  digitalWrite(chipWE, HIGH);
  digitalWrite(chipCE, LOW);
  digitalWrite(chipOE, LOW);
  delayMicroseconds(100);
  int has_error = 0;
  for (int i = 0; i < BUFF_LEN ; i++) {
    uint8_t rd = getByte(addr + i);
    if (pageBuffer[i] != rd) {
      Serial.print("0x");
      Serial.print(addr + i, HEX);
      Serial.print("=");
      Serial.print(rd, HEX);
      Serial.print(",not:");
      Serial.print(pageBuffer[i], HEX);
      Serial.print(" ");
      has_error = 1;
    }
  }
  if (has_error) {
    Serial.print("_type:");
    Serial.print(typee);
  }else{
      Serial.println("ok");
  }
  digitalWrite(chipCE, HIGH);
  digitalWrite(chipOE, HIGH);
}

// 28 29
void setByte(uint32_t address, uint8_t dat) {
  write_data_bus(dat);
  setAddress(address);
//  delayMicroseconds(1);
//  digitalWrite(chipCE, LOW);
  digitalWrite(chipWE, LOW);
//  delayMicroseconds(1);
  delay110ns;
  digitalWrite(chipWE, HIGH);
//  digitalWrite(chipCE, HIGH);
//  delayMicroseconds(1);
  delay110ns;
}


// 根据地址读取数据
byte getByte(uint32_t addr) {
  setAddress (addr);
  delayMicroseconds(1);
  return read_data_bus();
}

// write the special six-byte code to turn off software data protection
void disableWriteProtection29() {
  write_mode();                            // set data bus pins as output
  //reset
  digitalWrite(chipCE, LOW);
  setByte (0x0000, 0xf0);
  delay(1);
  setByte (0x5555, 0xaa);                     // write code sequence
  setByte (0x2aaa, 0x55);
  setByte (0x5555, 0x80);
  setByte (0x5555, 0xaa);
  setByte (0x2aaa, 0x55);
  setByte (0x5555, 0x20);
  digitalWrite(chipCE, LOW);
  read_mode();                             // release data bus (set as input)
  delay(100);                                  // wait write cycle time
  Serial.println("ok");
}

// write the special three-byte code to turn on software data protection
void enableWriteProtection29() {
  write_mode();                            // set data bus pins as output
  //reset
  digitalWrite(chipCE, LOW);
  setByte (0x0000, 0xf0);
  delayMicroseconds(1);
  setByte (0x5555, 0xaa);                     // write code sequence
  setByte (0x2aaa, 0x55);
  setByte (0x5555, 0xa0);
  digitalWrite(chipCE, HIGH);
  read_mode();                             // release data bus (set as input)
  delay(100);                                  // wait write cycle time
}

void eraser29() {
  digitalWrite(chipOE, HIGH);
  write_mode();                            // set data bus pins as output
  //reset
  digitalWrite(chipCE, LOW);
  setByte (0x0000, 0xf0);
  digitalWrite(chipCE, HIGH);
  
  delay(1);
  
  digitalWrite(chipCE, LOW);
  setByte (0x5555, 0xaa);                     // write code sequence
  setByte (0x2aaa, 0x55);
  setByte (0x5555, 0x80);
  setByte (0x5555, 0xaa);
  setByte (0x2aaa, 0x55);
  setByte (0x5555, 0x10);
  digitalWrite(chipCE, HIGH);
  delayMicroseconds(100);
  read_mode();                             // release data bus (set as input)
  delay(100);                                  // wait write cycle time
  Serial.println("ok");
}


void loop() {
  //程序判断用
  if (Serial.available()) {
    readCommand();
    char cmd = cmdBuffer[0];                      //指令
    uint32_t chipId    = hexChip(cmdBuffer + 2);  //芯片
    uint8_t  startPage = hexDigit(cmdBuffer[9]);
    uint32_t startAddr = hexWord(cmdBuffer + 10); //开始地址
    uint8_t  endPage   = hexDigit(cmdBuffer[15]);
    uint32_t endAddr   = hexWord(cmdBuffer + 16); //结束地址

    startAddr &= 0xffff;
    endAddr &= 0xffff;
    startPage &= 0x7;
    endPage   &= 0x7;
    endPage = endPage < startPage ? startPage : endPage;

    startAddr = (startPage * 65536) + startAddr;
    endAddr   = (endPage * 65536) + endAddr;

    endAddr = endAddr < startAddr ? startAddr : endAddr;

    // 数据长度
    uint32_t dataLength = endAddr - startAddr + 1;

    // 初始化地址
    setAddress(0);

    switch (cmd) {
      case 'i':   Serial.println(IDENT); break;
      case 'v':   Serial.println(VERSION); break;
      case 't':   Serial.println("ok"); break;
      //测试输入内容
      case 'r':   read29(startAddr, dataLength); break;
      case 'w':
        if (chipId == 2716 || chipId == 2732) { //27-24p
        } else if (chipId == 2764 || chipId == 27128 || chipId == 27256) { //27-28p
        } else if (chipId == 2864 || chipId == 28256) { //28
        } else if (chipId == 29011 || chipId == 29021 || chipId == 29041) { //29
          //winbond
          write29(startAddr, dataLength, 9);
        } else if (chipId == 29010 || chipId == 29020 || chipId == 29040) { //29
          write29(startAddr, dataLength, 29);
        } else {
          Serial.print(F("Write not support,chipId:"));
          Serial.println(chipId);
        }
        break;

      case 'c':   check(chipId); break;

      case 'u':   disableWriteProtection29(); break;
      case 'l':   enableWriteProtection29(); break;
      //29芯片擦除指令
      case 'e':   eraser29(); break;

      case 'x':   Serial.print(startAddr); Serial.print(" "); Serial.print(endAddr); Serial.print(" "); Serial.println(dataLength); break;

      case 'z':   
                for (uint8_t i = 0; i < BUFF_LEN; i++) {
                  while (!Serial.available());
                  pageBuffer[i] = Serial.read();
                }

                for (uint8_t i = 0; i < BUFF_LEN; i++) {
                  Serial.write(pageBuffer[i]);
                }
                Serial.println("");

                break;
      
      default:    break;
    }
    setAddress(0);
  }
}
